﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Pulse
{
    public enum Type
    {
        Explosion,
        Smoke
    };

    public class ParticleEngine
    {
        private List<Texture2D> textures;
        public RenderTarget2D particleTarget;

        // The max number of effects to draw in one Update.
        // Example of an effect would be smoke, which may have x numer of particles existing at any time
        private int howManyEffects;

        // The particles that exist on screen.
        public Particle[] particles;

        // The particles that do not exist on scren.
        public Queue<Particle> freeParticles;

        public int freeParticlesCount
        {
            get { return freeParticles.Count; }
        }

        // The world position of where the particles are created
        private Vector2 emitterLocation;
        public Vector2 EmitterLocation
        {
            get { return emitterLocation; }
            set { emitterLocation = value; }
        }

        public bool randomColor;

        private Random random;
        private Color defaultColor = Color.White;

        // These int values control how many particles are allowed to be 
        // generated every Update()
        public int maxParticlesGenerated = 20;
        public int minParticlesGenerated = 10;

        public float maxInitialVelocity = 2;
        public float minInitialVelocity = 1;

        public float maxLifetime = 40;
        public float minLifetime = 10;

        public float maxScale = 1.5f;
        public float minScale = 0.5f;

        // The type of particle system, such as explosive, clash, smoke, magic.
        private Type systemType; 
        public Type SystemType 
        { 
            get { return systemType; }
            set { systemType = value; }
        }

        public ParticleEngine(List<Texture2D> textures, Vector2 location, bool colorChoice,
            Type systemType, int maxEffects, int minNumberParticles, int maxNumberParticles)
        {
            this.textures = textures;
            this.EmitterLocation = location;
            this.randomColor = colorChoice;
            this.SystemType = systemType;
            this.howManyEffects = maxEffects;
            this.maxParticlesGenerated = maxNumberParticles;
            this.minParticlesGenerated = minNumberParticles;
            this.random = new Random();
            InitializeMemory();
        }

        public void InitializeMemory()
        {
            /* One effect may have multiple particles in it */
            particles = new Particle[howManyEffects * maxParticlesGenerated];
            freeParticles = new Queue<Particle>(howManyEffects * maxParticlesGenerated);

            for (int i = 0; i < particles.Length; i++)
            {
                particles[i] = new Particle();
                freeParticles.Enqueue(particles[i]);
            }
        }

        public void AddParticle()
        {
            int numParticles = random.Next(minParticlesGenerated, maxParticlesGenerated);

            for (int i = 0; i < numParticles && freeParticles.Count > 0; i++)
            {
                Particle p = freeParticles.Dequeue();
                InitilaizeDefaultParticle(p, EmitterLocation);
            }
        }

        public virtual void InitilaizeDefaultParticle(Particle p, Vector2 location)
        {
            float speed = RandomBetween(minInitialVelocity, maxInitialVelocity);
            Vector2 direction = PickRandomDirection();
            Vector2 newVelocity = direction * speed;

            float angularVelocity = 0.1f * (float)(random.NextDouble() * 2 - 1);
            int ttl = (int)RandomBetween(minLifetime, maxLifetime);
            float scale = RandomBetween(minScale, maxScale);
            float decay = 1/(float)ttl;

            Color color = getDefaultColor(randomColor);

            p.InitializeParticle(textures[random.Next(textures.Count)], location, newVelocity, 0.0f, angularVelocity, 
                color, decay, scale, ttl);
        }

        public float RandomBetween(float min, float max) 
        { 
            return min + (float)random.NextDouble() * (max - min); 
        }

        private Particle GenerateNewParticle()
        {
            Texture2D texture = textures[random.Next(textures.Count)];
            Vector2 position = EmitterLocation;
            Vector2 velocity = PickRandomDirection();
            float angle = 0;
            float angularVelocity = 0.1f * (float)(random.NextDouble() * 2 - 1);

            //Color color = new Color(
            //            (float)random.NextDouble(),
            //            (float)random.NextDouble(),
            //            (float)random.NextDouble());
            Color color = getDefaultColor(randomColor);

            float size = (float)random.NextDouble();
            float decayRate = 0.02f;
            int ttl = 20 + random.Next(40);

            return new Particle(texture, position, velocity, angle, angularVelocity, color, decayRate, size, ttl);
        }

        public void DeleteAllParticles()
        {
            foreach (Particle p in particles)
            {
                freeParticles.Enqueue(p);
            }
        }

        public Vector2 PickRandomDirection()
        {
            return new Vector2(
                2f * (float)(random.NextDouble() * 2 - 1),
                2f * (float)(random.NextDouble() * 2 - 1));
        }

        public Color getDefaultColor(bool randomChoice)
        {
            if (randomChoice)
                return new Color(
                        (float)random.NextDouble(),
                        (float)random.NextDouble(),
                        (float)random.NextDouble());
            else return defaultColor;
        }

        public void setDefaultColor(Color newColor)
        {
            defaultColor = newColor;
        }

        public void UpdateDrawOrder(int index, float order)
        {
            particles[index].DrawOrder = order;
        }

        public void Update(bool activate)
        {
            if (systemType.Equals(Type.Smoke) && activate)
                AddParticle();

            foreach (Particle p in particles)
            {
                if (p.isAlive)
                {
                    p.Update();
                    if (!p.isAlive)
                        freeParticles.Enqueue(p);
                }
            }
        }

        public void Draw(SpriteBatch spritebatch)
        {
            foreach (Particle p in particles)
            {
                if (!p.isAlive)
                    continue;

                p.Draw(spritebatch);
            }
        }
    }


}
